
/*
 *  ne2k.c
 *  xcode
 *
 *  Created by Emiel Goor,van on 24-01-08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */
#include "ne2k.h"
#include "pci.h" 
#include "monitor.h"
#include "paging.h"
#include "kheap.h"
#include "task.h"
#include "common.h"
#include "eth.h"

// commands for ne2k
#define NE_RESET	0x1f
#define NE_DATA		0x10

// pci information for the card
struct nePciTable
{
	u16int vendor;
	u16int device;
} nePciTable[] = 
{
	{ 0x10ec, 0x8029 },
};

// ports for ISA detection
static u32int nePortList[] = 
	{0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0};

// list of cards
static struct ne2000_t* neDevs[250];
int neCount = 0;

// gets a pointer to one of the entries
struct ne2000_t* getNeCard( int i )
{
	return neDevs[i];
}

// sets up the card
static void neSetup( struct ne2000_t *data )
{
	// Reset the card
 
	outb(data->ioAddress + NE_RESET, inb(data->ioAddress + NE_RESET));
	
	while((inb(data->ioAddress + INTERRUPTSTATUS) & 0x80) == 0);
	outb(data->ioAddress + INTERRUPTSTATUS, 0xff);

	outb(data->ioAddress + COMMAND, 0x21 );
	outb(data->ioAddress + DATACONFIGURATION, 0x48 );
	outb(data->ioAddress + REMOTEBYTECOUNT0, 0 );
	outb(data->ioAddress + REMOTEBYTECOUNT1, 0 );
	outb(data->ioAddress + RECEIVECONFIGURATION, 0x0E );
	outb(data->ioAddress + TRANSMITCONFIGURATION, 0x00 );
	outb(data->ioAddress + TRANSMITPAGE, 0x40);
	outb(data->ioAddress + PAGESTART, 0x46);
	outb(data->ioAddress + BOUNDARY, 0x46);
	outb(data->ioAddress + PAGESTOP, 0x60);
	outb(data->ioAddress + INTERRUPTSTATUS, 0xff);
	outb(data->ioAddress + INTERRUPTMASK, 0x1f);
	outb(data->ioAddress + COMMAND, 0x61);
	outb(data->ioAddress + CURRENT, data->nextPacket = PSTART + 1);
	outb(data->ioAddress + COMMAND, 0x22);
	outb(data->ioAddress + TRANSMITCONFIGURATION, 0x00 );
}

// gets the hardware address of a device
static void neGetHardwareAddress(struct ne2000_t *data)
{
	int wordlen = 2;
	int i;
	uint8_t buffer[32];

	outb(data->ioAddress + REMOTEBYTECOUNT0, 0x20);
	outb(data->ioAddress + REMOTEBYTECOUNT1, 0x00);

	outb(data->ioAddress + REMOTESTARTADDRESS0, 0x00);
	outb(data->ioAddress + REMOTESTARTADDRESS1, 0x00);

	outb(data->ioAddress + COMMAND, E8390_START | E8390_NODMA | E8390_PAGE0);

	for(i=0; i<32; i+=2)
	{
		buffer[i] = inb(data->ioAddress + IOPORT);
		buffer[i+1] = inb(data->ioAddress + IOPORT);

		if(buffer[i] != buffer[i+1])
			wordlen = 1;
	}

	if(wordlen == 2)
	{
		data->wordMode = 1;
		for(i=0; i<16; i++)
			data->saprom[i] = buffer[i*2];
		
		outb(data->ioAddress + DATACONFIGURATION, dcr);
		return;
	}
	else
	{
		data->wordMode = 0;
	}
}

// probes a port (used by ISA probe)
static int neProbePort(u16int ioAddress)
{
	int reg0 = inb(ioAddress);
	int regd;

	if(reg0 == 0xff)
		return -1;
	
	outb(ioAddress + COMMAND, E8390_NODMA + E8390_PAGE1 + E8390_STOP);
	regd = inb(ioAddress + 0x0d);
	outb(ioAddress + 0x0d, 0xff);
	outb(ioAddress + COMMAND, E8390_NODMA + E8390_PAGE0);
	inb(ioAddress + FAE_TALLY);

	if(inb(ioAddress + FAE_TALLY) != 0)
	{
		outb(ioAddress, reg0);
		outb(ioAddress + 0x0d, regd);
		return -1;
	}
	return 0;
}

// probes the ISA for a ne2k card
static struct ne2000_t *neProbeISA()
{
	int i, j, k;
	struct ne2000_t *ne;

	for( i = 0; nePortList[i] != 0; i++ )
	{
		k=0;
		for( j = 0; j < neCount; j++ )
		{
			if( neDevs[j]->ioAddress == nePortList[i] );
			{
				k=1;
				break;
			}
		}
		if( k == 1 )
			continue;

		if( neProbePort( nePortList[i] ) == 0 )
		{
			ne = ( struct ne2000_t * ) kmalloc_a( sizeof( struct ne2000_t ) );
			if( ! ( ne ) )
				return (struct ne2000_t*) NULL;
			
			// FIXME: This IRQ is hardcoded into bochsrc right now
			//        We need to detect the IRQ somehow.
			ne->irq = 9;
			ne->ioAddress = nePortList[i];
			return ne;
		}
	}
	return (struct ne2000_t*) NULL;
}

// probes the PCI for a ne2k card
static struct ne2000_t *neProbePCI(void)
{
	struct ne2000_t *ne;
	pciDev_t *pDev;
	int i;

	for(i=0; i < sizeof( nePciTable ); i+=4 )
		pDev = pciGetDeviceById(nePciTable[i].vendor, 
					nePciTable[i].device, neCount+1);
	
	if(!pDev)
		return 0;

	ne = kmalloc_a( sizeof( struct ne2000_t ) );
	if(!ne)
		return 0;

	ne->ioAddress = pDev->base[0] & ~3;
	ne->irq = pDev->irq;	
	return ne;
}

// reads a packet
packet_t *readPacket(struct ne2000_t *ne)
{
	packet_t *pkt;
	uint8_t curr;
	int io = ne->ioAddress;
	uint8_t rsr, next;
	int i, len;
	char *data;

		outb(io + REMOTEBYTECOUNT0, 4);
		outb(io + REMOTEBYTECOUNT1, 0);
		outb(io + REMOTESTARTADDRESS0, 0);
		outb(io + REMOTESTARTADDRESS1, ne->nextPacket);
		outb(io + COMMAND, E8390_RREAD | E8390_START);
		rsr = inb(io + NE_DATA);
		next = inb(io + NE_DATA);
		len = inb(io + NE_DATA);
		len += inb(io + NE_DATA) << 8;
		outb(io + INTERRUPTSTATUS, 0x40);

		if((rsr & 31) == 1 && next >= PSTART && next <= PSTOP && len <= 1532)
		{
			data = kmalloc_a(len);
			outb(io + REMOTEBYTECOUNT0, len);
			outb(io + REMOTEBYTECOUNT1, len >> 8);
			outb(io + REMOTESTARTADDRESS0, 4);
			outb(io + REMOTESTARTADDRESS1, ne->nextPacket);
			outb(io + COMMAND, E8390_RREAD | E8390_START);
			for(i=0; i<len; ++i)
				data[i] = inb(io + NE_DATA);

			outb(io + INTERRUPTSTATUS, 0x40);

			ne->nextPacket = (next == PSTOP) ? PSTART : next;
		}	
		if(ne->nextPacket == PSTART)
			outb(io + BOUNDARY, PSTOP-1);
		else
			outb(io + BOUNDARY, ne->nextPacket-1);
	
		pkt = packetCreate(data, len);
		kfree(data);
		
	return pkt;
}

// writes a packet
int writePacket(struct ethernet_t *eth, packet_t *pkt)
{
	struct ne2000_t *ne;
	char *data;
	int size, io, i = 0;

	if(!eth || !eth->data)
		return 0;

	ne = (struct ne2000_t *)eth->data;
	
	if(!pkt || !pkt->data)
		return 0;
	data = pkt->data;
	size = pkt->len;
	io = ne->ioAddress;
	
	outb(io + REMOTEBYTECOUNT0, size);
	outb(io + REMOTEBYTECOUNT1, size >> 8);
	outb(io + REMOTESTARTADDRESS0, 0);
	outb(io + REMOTESTARTADDRESS1, TRANSMITBUFFER);
	outb(io + COMMAND, E8390_RWRITE | E8390_START);
	while(i < size)
	{
		outb(io + NE_DATA, data[i]);
		i++;
	}

	outb(io + TRANSMITPAGE, TRANSMITBUFFER);
	outb(io + TRANSMITBYTECOUNT0, size);
	outb(io + TRANSMITBYTECOUNT1, size >> 8);
	outb(io + COMMAND, E8390_NODMA | E8390_TRANS | E8390_START);
		
	// return the amount of data read
	
	return i;
}

/**
1. check ISR, if says no packets received, we're done 
2. reset the ISR bit about packets received 
3. read CURRENT pointer from Page1. 
4. read packets until "next packet" of the last packet = CURRENT value that was read at step 3 
5. Goto 1 (in case we got more packets while reading) 
**/

// IRQ handler for NE2K card
static void neIrqHandler( registers_t r )
{
	printf("Whoeet got NE2K INTERRUPT \n ");
	struct ne2000_t* ne = neDevs[0];
	uint8_t status;
	packet_t *p;
	
	// main loop
/*	while( 1 )
	{
		// check ISR bit
		unsigned char isr = inb( ne->ioAddress + INTERRUPTSTATUS );
		outb( ne->ioAddress + INTERRUPTSTATUS, isr );
		
		// is it set for receive?
		if( status & 0x01 )
		{			
			// received
			p = readPacket( ne );
			handleEthernet( ne->eth, p );
		}
		else
		{
			// nope
			break;
		}
	}*/
	
	while(1)
	{
		status = inb(ne->ioAddress + INTERRUPTSTATUS);
		outb(ne->ioAddress + INTERRUPTSTATUS, status);
		
		if( status & 0x1 )
		{
			printf( "NE2000: Packet Recieved\n" );
			ne->packrecv++;
			p = readPacket(ne);
			
			//TODO
			handleEthernet(ne->eth, p);
		}
		else if( status & 0xa )
		{
			ne->packsend++;
			printf( "NE2000: Packet Transmitted\n" );
		}
		else
		{
			printf("got interrupt.....");
			break;
		}
	}
}

// initializes the card
void InitNe2k()
{
	// print his info - do not remove
	//printf( "NE 2000 Ethernet Card Driver\nCopyright 2007 Anthony Lineberry\nLicensed under the GPL\n" );
	
	// the ne2000 card
	struct ne2000_t* neData;
	
	// ethernet data
	struct ethernet_t* eth;
	
	// probe the isa for it
	neData = neProbePCI();
	
	// if we can't find it, probe the pci
	if( ! ( neData ) )
	{
		// not on PCI, detect on ISA
		neData = neProbeISA();
	}
	
	// check if it's valid
	if( ! ( neData ) )
	{
		// no ne2k card at all
		printf( "No ne2k card found.\n" );
		return;
	}
	
   		neGetHardwareAddress(neData);
   		neSetup(neData);
   		
	// setup the card
	neSetup( neData );
	
	// set the ethernet
	eth = (struct ethernet_t*) kmalloc_a(sizeof(struct ethernet_t));
	
	// check for validity
	if( ! ( eth ) )
		return;
	
	// set it up
	neData->eth = eth;
	neDevs[neCount++] = neData;
	memcpy( eth->macAddr, neData->saprom, sizeof( eth->macAddr ) );
	eth->data = (void*) neData;
	eth->type = NE2000_TYPE;
	neData->packrecv = 0;
	neData->packsend = 0;

	// tell me info
	printf( "NE2K Comapatible NIC, iobase=0x%x, irq %d, ", neData->ioAddress, neData->irq );
	printf( "[%x:%x:%x:%x:%x:%x]\n", eth->macAddr[0], eth->macAddr[1], eth->macAddr[2],
														eth->macAddr[3], eth->macAddr[4],
														eth->macAddr[5] );
	
	// set send data
	eth->send = writePacket;
	
	// add the card
	if( addEthernetCard( eth ) )
	{	
		printf("Registering NE2k device... \n");
		// register the irq
		register_interrupt_handler( neData->irq, &neIrqHandler );
		
		// clear the ARP cache
		ClearCache();
		
		// clear the wait list
		InitArpReplyWait();
	}
	else
	{
		printf( "Failed to add NE2K card.\n" );
		return;
	}
}
